How I Build a Rust Backend Service
In early 2021, I built a prototype Rust backend service at work. I knew Rust was powerful and ergonomic. It could fly through difficult computation and language features like pattern matching and idiomatic Result and Option enums made it fresh, new, and exciting.
Honestly, it's a language that's hard not to fall in love with. But I had only used it then for game development, so it was a big change to serve a REST API, publish and consume Kafka messages, and talk to a database. On top of that, it seemed like no one else was using Rust for web applications.
The weekly newsletter, This Week in Rust, still described Rust as a "systems language" (though the description did change later that year). There was a lot of trailblazing to do. Here's everything I learned and how I build Rust backend services now.
Architectural Philosophy
The first thing to recognize is that Rust isn’t going to hold your hand on the big-picture architecture of your application. If you’re writing an application in Java, odds are you’re using a framework like Spring Boot, which provides some guidance for laying out your code. Or if you’re writing Ruby, you’ll probably use Rails.
Rails is quite opinionated about file structure, how models should look, and so on. But Rust doesn’t have a ubiquitous do-everything-for-me framework like Java or Ruby. As a matter of culture, Rust crates are small. They’re intended to do one thing and do it well. That means you have the freedom to pick an HTTP server, database, and observability crate and put it all together however you’d like.
With so much freedom, it’s really easy to shoot yourself in the foot. To keep my feet intact, I started by researching architectural paradigms and settling on Bob Martin’s Clean Architecture. One of his big ideas is that dependencies should point in the direction of stability (that is to say, depend on things that don’t change) since changes often ripple down the dependency chain.
You want to minimize the number of additional changes your changes will cause. If the number of changes isn’t kept in check, codebases become more and more time-consuming…